package com.wb.spring.boot.autoconfigure.proxy;

import com.wb.spring.boot.autoconfigure.annotation.WbClient;
import com.wb.spring.boot.autoconfigure.exception.RpcException;
import com.wb.spring.boot.autoconfigure.stereotype.WbRequestBody;
import com.wb.spring.boot.autoconfigure.stereotype.WbRequestMapping;
import com.wb.spring.boot.autoconfigure.stereotype.WbRequestParam;
import com.wb.spring.boot.autoconfigure.utils.CommonUtil;
import javassist.*;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.annotation.Annotation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 调用代理对象
 *
 * @author deeprado
 * @date 2022/9/2 17:26
 */
public class WbClientProxyExample {

    static Logger logger = LoggerFactory.getLogger(WbClientProxyExample.class);

    /**
     * 生成的代理对象名称前缀
     */
    private static final String PROXY_PREFIX = "WbRpcProxy";

    /**
     * 生成的代理对象名称后缀
     */
    private static final String PROXY_SUFFIX = "Impl";

    /**
     * 参数为@WbRequestBody的类型
     */
    private static final String REQUEST_BODY_PARAMETER = "REQUEST_BODY_PARAMETER";

    public static <T> Class<T> buildProxy(Class<T> t) throws NotFoundException, CannotCompileException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException {
        // 获取调用的服务名称
        WbClient annotation = t.getAnnotation(WbClient.class);
        ClassPool pool = ClassPool.getDefault();

        String serviceClassName = t.getName();
        // 服务名
        String clientServerName = t.getAnnotation(WbClient.class).name();
        //创建代理类对象
        String implClassName = getImplName(t);
        CtClass ctClass = pool.makeClass(implClassName);
        ctClass.setModifiers(Modifier.PUBLIC);

        //设置代理类的接口
        CtClass interj = pool.getCtClass(serviceClassName);
        interj.setModifiers(Modifier.PUBLIC);

        // 设置接口
        CtClass[] interfaces = new CtClass[]{interj};
        ctClass.setInterfaces(interfaces);

        // 生成实例，并调用 TODO 增加配置判断
        Class<?> fallback = annotation.fallback();
        Class<?> fallbackFactory = annotation.fallbackFactory();

        // 设置方法的属性
        initClassField(ctClass, pool);
        boolean fallbackEnabled = initClassFallbackField(ctClass, t, fallback, pool);
        boolean fallbackFactoryEnabled = initClassFallbackFactoryField(ctClass, t, fallbackFactory, pool);

        //代理类的所有方法
        CtMethod[] methods = interj.getDeclaredMethods();
        // 获取所有方法的参数信息
        Map<String, List<String>> parameterMap = buildParametersMap(t);

        for (CtMethod method : methods) {
            // 创建代理类的方法
            CtMethod ctMethod = initClassMethod(method, ctClass, parameterMap, t, clientServerName, fallbackEnabled, fallbackFactoryEnabled);
            //
            ctClass.addMethod(ctMethod);
        }
        ctClass.writeFile("D:\\Tmp\\xx");
        return (Class) ctClass.toClass();
    }


    /**
     * 创建类的属性
     *
     * @param ctClass       生成的代理类
     * @param fallbackClass fallback类
     * @param pool
     * @return
     * @throws CannotCompileException
     * @throws NotFoundException
     */
    private static boolean initClassFallbackField(CtClass ctClass, Class<?> interfaceClass, Class<?> fallbackClass, ClassPool pool) throws CannotCompileException, NotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException {
        ConstPool constPool = ctClass.getClassFile().getConstPool();

        // 添加Autowired注解，后续注册到spring中会自动装配
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation filedAnnotation = new Annotation(Autowired.class.getName(), constPool);
        annotationsAttribute.addAnnotation(filedAnnotation);

        boolean fallbackEnabled = false;
        // 注册服务
        if (fallbackClass != void.class) {
            String beanName = CommonUtil.lowerFirst(fallbackClass.getSimpleName());
            String fallbackClassName = fallbackClass.getName();

            pool.importPackage(fallbackClassName);

            Class<?>[] interfaces = fallbackClass.getInterfaces();
            List<String> collectInterfaceNames = Arrays.stream(interfaces).map(interfaceclass -> interfaceclass.getName()).filter(interfaceclass -> interfaceclass.equals(interfaceClass.getName())).collect(Collectors.toList());
            if (collectInterfaceNames.isEmpty()) {
                //
                throw new RpcException("不是实现类");
            }

            CtClass fallbackCtClass = pool.get(fallbackClassName);
            CtField fallbackField = new CtField(fallbackCtClass, beanName, ctClass);
            fallbackField.setModifiers(AccessFlag.PUBLIC);
//            FieldInfo fallbackFieldInfo = fallbackField.getFieldInfo();
//            fallbackFieldInfo.addAttribute(annotationsAttribute);

            ctClass.addField(fallbackField, "new " + fallbackClassName + "()");
//            ctClass.addField(fallbackField);

            fallbackEnabled = true;

        }

        return fallbackEnabled;
    }

    /**
     * @param ctClass
     * @param t
     * @param fallbackFactoryClass fallback工厂类
     * @param pool
     * @return
     * @throws CannotCompileException
     * @throws NotFoundException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
     */
    private static boolean initClassFallbackFactoryField(CtClass ctClass, Class<?> t, Class<?> fallbackFactoryClass, ClassPool pool) throws NotFoundException, CannotCompileException {
        ConstPool constPool = ctClass.getClassFile().getConstPool();

        // 添加Autowired注解，后续注册到spring中会自动装配
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation filedAnnotation = new Annotation(Autowired.class.getName(), constPool);
        annotationsAttribute.addAnnotation(filedAnnotation);

        boolean fallbackFactoryEnabled = false;

        String interfaceClassName = t.getName();

        if (fallbackFactoryClass != void.class) {

            //
            String beanName = CommonUtil.lowerFirst(fallbackFactoryClass.getSimpleName());
            String fallbackFactoryClassName = fallbackFactoryClass.getName();

//            pool.importPackage(fallbackFactoryClassName);

            CtClass fallbackFactoryCtClass = pool.get(fallbackFactoryClassName);
//            fallbackFactoryCtClass.addConstructor(CtNewConstructor.defaultConstructor(fallbackFactoryCtClass));

//            CtMethod create = fallbackFactoryCtClass.getDeclaredMethod("create");
//            if (create.isEmpty()) {
//                //
//                throw new RpcException("方法不存在");
//            }
//            CtClass returnTypeCtClass = create.getReturnType();
//            if (!returnTypeCtClass.getName().equals(interfaceClassName)) {
//                throw new RpcException("不是实现类");
//            }

//            CtField fallbackFactoryField = new CtField(pool.get(fallbackFactoryClassName), beanName, ctClass);
//            fallbackFactoryField.setModifiers(AccessFlag.PUBLIC);
//            FieldInfo fallbackFactoryInfo = fallbackFactoryField.getFieldInfo();
//            fallbackFactoryInfo.addAttribute(annotationsAttribute);
//
//            ctClass.addField(fallbackFactoryField, "new " + fallbackFactoryClassName + "()");
//            ctClass.addField(fallbackFactoryField);


//            // 加载工厂类
//            pool.importPackage(fallbackFactoryClassName);
//
//            CtField fallbackFactoryField = new CtField(fallbackFactoryCtClass, "fallbackFactory", ctClass);
//            fallbackFactoryField.setModifiers(AccessFlag.PUBLIC);
//            ctClass.addField(fallbackFactoryField, "new " + fallbackFactoryClass.getSimpleName() + "();");
//


            // 创建接口代理类对象
            String implClassName = getImplFallbackName(t);
            CtClass ctClass2 = pool.makeClass(implClassName);
            ctClass2.setModifiers(Modifier.PUBLIC);

            //代理类的所有方法
            CtClass interj = pool.getCtClass(t.getName());
            CtClass[] interfaces = new CtClass[]{interj};
            ctClass2.setInterfaces(interfaces);

            CtMethod[] methods = interj.getDeclaredMethods();

            // 获取接口对象
            CtClass[] nestedClasses = fallbackFactoryCtClass.getNestedClasses();
            Arrays.stream(nestedClasses).forEach((e) -> {
                if (e.getName().equals(fallbackFactoryClassName + "$1")) {
//                    try {
//                        ctClass2.setSuperclass(e);
//                    } catch (CannotCompileException ex) {
//                        throw new RuntimeException(ex);
//                    }

                    //
                    CtMethod[] declaredMethods = e.getDeclaredMethods();
                    for (CtMethod method : declaredMethods) {
                        try {
                            CtMethod method2 = CtNewMethod.copy(method, ctClass2, null);
                            ctClass2.addMethod(method2);
                        } catch (CannotCompileException ex) {
                            throw new RuntimeException(ex);
                        }

//                        try {
//                            // 创建代理类的方法
//                            String methodName = method.getName();
//                            CtMethod cm = new CtMethod(method.getReturnType(), methodName, method.getParameterTypes(), ctClass2);
//                            cm.setModifiers(Modifier.PUBLIC);
//
//                            String returnType = "void".equals(cm.getReturnType().getName()) ? "com.wb.spring.boot.autoconfigure.proxy.Void" : cm.getReturnType().getName();
//                            String isReturn = "com.wb.spring.boot.autoconfigure.proxy.Void".equals(returnType) ? "" : "return null;";
//                            cm.setBody(method.get);
//                            ctClass2.addMethod(method);
//                        } catch (Exception e2) {
//                            e2.printStackTrace();
//                        }
                    }
                }
            });
//            for (CtMethod method : methods) {
//                // 创建代理类的方法
//                String methodName = method.getName();
//                CtMethod cm = new CtMethod(method.getReturnType(), methodName, method.getParameterTypes(), ctClass2);
//                cm.setModifiers(Modifier.PUBLIC);
//
//                String returnType = "void".equals(cm.getReturnType().getName()) ? "com.wb.spring.boot.autoconfigure.proxy.Void" : cm.getReturnType().getName();
//                String isReturn = "com.wb.spring.boot.autoconfigure.proxy.Void".equals(returnType) ? "" : "return null;";
//                cm.setBody("{System.out.println(\"bbbbbb\"); " + isReturn + "}");
//                ctClass2.addMethod(cm);
//            }
            // 添加默认的构造函数
            ctClass2.addConstructor(CtNewConstructor.defaultConstructor(ctClass2));

            CtField fallbackField = new CtField(ctClass2, beanName, ctClass);
            fallbackField.setModifiers(AccessFlag.PUBLIC);

            ctClass.addField(fallbackField, "new " + implClassName + "()");
            ctClass2.toClass();

//            String createFallback = "public " + interfaceClass.getSimpleName() + " fallback = fallbackFactory.create()";
//            ctClass.addField(CtField.make(createFallback, ctClass));
//            // 获取接口对象
//            Object o = fallbackFactoryClass.getConstructor().newInstance();
//            Method method = fallbackFactoryClass.getMethod("create");
//            Object fallback2 = method.invoke(o);
//            //
//            Class<?> proxyClass = fallback2.getClass();

//            CtField fallbackField = new CtField(returnType, "fallback", ctClass);
//            fallbackField.setModifiers(AccessFlag.PUBLIC);

//            String createFallback = "public " + interfaceClassName+ " fallback ;";
//            ctClass.addField(CtField.make(createFallback, ctClass));

//            String createFallback = "public " + interfaceClassName + " fallback = new " + fallbackFactoryClassName + "().create() ;";
//            ctClass.addField(CtField.make(createFallback, ctClass));

//            CtClass fallbackFactoryProxyCtClass = pool.makeClass(fallbackFactoryClass.getName() + "JavassistProxy");
//            fallbackFactoryProxyCtClass.setSuperclass(pool.get(fallbackFactoryClass.getName()));
//            // 添加默认的构造函数
//            fallbackFactoryProxyCtClass.addConstructor(CtNewConstructor.defaultConstructor(fallbackFactoryProxyCtClass));
//            // 添加属性
//            fallbackFactoryProxyCtClass.addField(CtField.make("public " + fallbackFactoryClassName
//                    + " realFactory = new " + fallbackFactoryClassName + "()", fallbackFactoryProxyCtClass));
//            // 添加方法，里面进行动态代理逻辑
//            fallbackFactoryProxyCtClass.addMethod(CtMethod.make("public " + returnType.getName() + " create() {return \"before \" + realFactory.create() + \" after\";}", fallbackFactoryProxyCtClass));


//            CtField fallbackFactoryField = new CtField(fallbackFactoryCtClass, "fallbackFactory", ctClass);
//            fallbackFactoryField.setModifiers(AccessFlag.PUBLIC);
//            ctClass.addField(fallbackFactoryField, "new " + fallbackFactoryClass.getName() + "();");
//
//            CtField fallbackField = new CtField(returnType, "fallback", ctClass);
//            fallbackField.setModifiers(AccessFlag.PUBLIC);
//            ctClass.addField(fallbackField, "fallbackFactory.create();");

            fallbackFactoryEnabled = true;

        }

        return fallbackFactoryEnabled;
    }

    private static CtClass initClassField(CtClass ctClass, ClassPool pool) throws CannotCompileException, NotFoundException {

        // 获取常量池
        ConstPool constPool = ctClass.getClassFile().getConstPool();

        // 添加Autowired注解，后续注册到spring中会自动装配
        AnnotationsAttribute annotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        Annotation filedAnnotation = new Annotation(Autowired.class.getName(), constPool);
        annotationsAttribute.addAnnotation(filedAnnotation);

        // 创建属性，调用远程服务使用
        CtField ctField = new CtField(pool.get("com.wb.spring.boot.autoconfigure.proxy.WbClientCall"), "clientCall", ctClass);
        ctField.setModifiers(AccessFlag.PUBLIC);
        FieldInfo fieldInfo = ctField.getFieldInfo();
        fieldInfo.addAttribute(annotationsAttribute);

        // 注册服务
        CtField registerField = new CtField(pool.get("com.wb.spring.boot.autoconfigure.register.IRegister"), "register", ctClass);
        registerField.setModifiers(AccessFlag.PUBLIC);
        FieldInfo registerFieldInfo = registerField.getFieldInfo();
        registerFieldInfo.addAttribute(annotationsAttribute);


        // 加入到类文件中
        ctClass.addField(ctField);
        ctClass.addField(registerField);

        return ctClass;
    }


    private static CtMethod initClassMethod(CtMethod method, CtClass ctClass, Map<String,
            List<String>> parameterMap, Class<?> t, String clientServerName, boolean fallbackEnabled, boolean fallbackFactoryEnabled) throws NotFoundException, ClassNotFoundException, CannotCompileException {
        String methodName = method.getName();
        CtMethod cm = new CtMethod(method.getReturnType(), methodName, method.getParameterTypes(), ctClass);
        cm.setModifiers(Modifier.PUBLIC);

        WbClient annotation = t.getAnnotation(WbClient.class);
        Class<?> fallback = annotation.fallback();
        Class<?> fallbackFactory = annotation.fallbackFactory();

        // 获取参数列表名称
        String methodFullName = buildMethodFullName(method);
        // 该方法参数的映射值
        List<String> parameterNames = parameterMap.get(methodFullName);
        // 解析访问的路径
        WbRequestMapping requestMapping = (WbRequestMapping) method.getAnnotation(WbRequestMapping.class);
        String requestMappingValue = requestMapping.value();

        StringBuilder url = new StringBuilder(requestMappingValue);

        // 请求参数
        StringBuilder uriVariablesStringBuilder = new StringBuilder("java.util.Map/*<String, Object>*/ uriVariables = new java.util.HashMap/*<>*/(); \n");

        StringBuilder fallbackCallParamsBuilder = new StringBuilder("");

        boolean containSymbol = false;
        String requestBody = "null";
        if (!CollectionUtils.isEmpty(parameterNames)) {
            String connect = "?";
            int size = parameterNames.size();
            for (int i = 0; i < size; i++) {
                String parameterName = parameterNames.get(i);
                if (StringUtils.EMPTY.equals(parameterName)) {
                    System.err.println(t.getName() + "类中，" + method.getName() + "方法的第" + (i + 1) + "参数没有加WbRequestParam注解，无法匹配，请添加WbRequestParam，或在打包时，使用-parameters，可根据参数名自动匹配。");
                    continue;
                }

                if (i + 1 < size) {
                    fallbackCallParamsBuilder.append("$" + (i + 1) + ",");
                } else {
                    fallbackCallParamsBuilder.append("$" + (i + 1));
                }

                if (REQUEST_BODY_PARAMETER.equals(parameterName)) {
                    requestBody = "$" + (i + 1);
                    continue;
                }
                uriVariablesStringBuilder.append("uriVariables.put(\"" + parameterName + "\", $" + (i + 1) + "); \n");


                containSymbol = true;
                if ("?".equals(connect)) {
                    url = new StringBuilder(url + connect + parameterName + "=\"+" + "$" + (i + 1));
                } else {
                    url.append("+\"").append(connect).append(parameterName).append("=\"+").append("$").append(i + 1);
                }
                connect = "&";
            }
        }

        if (!containSymbol) {
            url = new StringBuilder(url + "\"");
        }
        uriVariablesStringBuilder.append("\n");

        // 默认http请求
        String http = "http://";

        String returnType = "void".equals(cm.getReturnType().getName()) ? "com.wb.spring.boot.autoconfigure.proxy.Void" : cm.getReturnType().getName();
        String isReturn = "com.wb.spring.boot.autoconfigure.proxy.Void".equals(returnType) ? "" : "return (" + returnType + ")";

        StringBuilder fallbackCallBuilder = new StringBuilder("\n");
        fallbackCallBuilder.append(isReturn);
        if (fallbackEnabled) {
            fallbackCallBuilder.append(CommonUtil.lowerFirst(fallback.getSimpleName()) + "." + methodName);
        } else if (fallbackFactoryEnabled) {
            fallbackCallBuilder.append(CommonUtil.lowerFirst(fallbackFactory.getSimpleName()) + "." + methodName);
//            fallbackCallBuilder.append( CommonUtil.lowerFirst(fallbackFactory.getSimpleName()) + ".create()." + methodName);
        }
        fallbackCallBuilder.append("(");
        fallbackCallBuilder.append(fallbackCallParamsBuilder);
        fallbackCallBuilder.append(")");
        String fallbackCall = fallbackCallBuilder + ";\n";

        String methodCall = uriVariablesStringBuilder + "" + isReturn + " clientCall.call(\"" + http + "\" + register.getServer(\"" + clientServerName + "\") + \"" + url + ",\"" + requestMapping.method() + "\", " + requestBody + ",uriVariables, Class.forName(\"" + returnType + "\")); \n";

        String cmBody = "{\n" +
                "" + methodCall +
                "\n}";
        if (fallbackEnabled || fallbackFactoryEnabled) {
            cmBody = ("{\n" +
                    "  try {\n" +
                    "" + methodCall +
                    "  \n} catch (Exception e) {\n" +
                    "" + fallbackCall +
                    "  \n} \n" +
                    "}");
        }
        cm.setBody(cmBody);
        return cm;
    }


    /**
     * 获取全限定名称
     *
     * @param method
     * @return
     * @throws NotFoundException
     */
    private static String buildMethodFullName(CtMethod method) throws NotFoundException {
        StringBuilder methodFullName = new StringBuilder(method.getName());
        if (method.getParameterTypes() != null) {
            for (CtClass parameterType : method.getParameterTypes()) {
                methodFullName.append(parameterType.getName());
            }
        }

        return methodFullName.toString();
    }

    /**
     * key-方法名称+方法参数类型
     * value-方法参数名称集合
     *
     * @param clazz 要解析的类
     * @return 方法的类对应参数名称
     */
    private static Map<String, List<String>> buildParametersMap(Class<?> clazz) {
        // 同名参数映射使用 此处使用1.8之上的--parameters才能支持，否则只能使用注解映射。
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        Map<String, List<String>> parameterMap = new HashMap<>(8);
        for (Method method : clazz.getMethods()) {
            StringBuilder name = new StringBuilder(method.getName());
            List<String> parameterRequestNames = new ArrayList<>();
            if (method.getParameters() != null) {
                for (Parameter parameter : method.getParameters()) {
                    name.append(parameter.getType().getName());
                    WbRequestParam annotation = parameter.getAnnotation(WbRequestParam.class);
                    String parameterRequestName = StringUtils.EMPTY;
                    if (annotation != null) {
                        parameterRequestName = annotation.value();
                    }
                    WbRequestBody wbRequestBody = parameter.getAnnotation(WbRequestBody.class);
                    if (wbRequestBody != null) {
                        parameterRequestName = REQUEST_BODY_PARAMETER;
                    }
                    parameterRequestNames.add(parameterRequestName);
                }
            }

            String[] parameterNames = discoverer.getParameterNames(method);
            if (parameterNames == null) {
                parameterMap.put(name.toString(), parameterRequestNames);
                continue;
            }
            List<String> correctParameterRequestNames = new ArrayList<>();
            for (int i = 0; i < parameterRequestNames.size(); i++) {
                String parameterRequestName = parameterRequestNames.get(i);
                if (StringUtils.EMPTY.equals(parameterRequestName)) {
                    correctParameterRequestNames.add(parameterNames[i]);
                    continue;
                }
                correctParameterRequestNames.add(parameterRequestName);
            }

            parameterMap.put(name.toString(), correctParameterRequestNames);
        }

        return parameterMap;
    }

    /**
     * 获取代理类的名称
     *
     * @param t 实现类类型
     * @return 实现类名称
     */
    private static <T> String getImplName(Class<T> t) {
        return t.getPackage().getName() + "." + PROXY_PREFIX + t.getSimpleName() + PROXY_SUFFIX;
    }

    /**
     * @param t
     * @param <T>
     * @return
     */
    private static <T> String getImplFallbackName(Class<T> t) {
        return t.getPackage().getName() + "." + PROXY_PREFIX + t.getSimpleName() + "Fallback" + PROXY_SUFFIX;
    }

}
